package server.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

import messages.ClientServerChatMessage;
import messages.ServerClientChatMessage;
import messages.ServerParameters;
import messages.ServerServerMessage;

public class ClientThread extends Thread implements ClientThreadIF {
	private Socket socket;
	private ObjectInputStream sInput;
	private ObjectOutputStream sOutput;
	private int id;
	private String username;
	private ClientServerChatMessage cm;
	private ServerClientIF server;
	private String date;

	public ClientThread(Socket socket, ServerClientIF server, int id,
			ObjectInputStream sInput) {
		this.id = id;
		this.socket = socket;
		this.server = server;
		this.sInput = sInput;
//		System.out.println("Thread trying to create Object Input/Output Streams");
		try {
			sOutput = new ObjectOutputStream(socket.getOutputStream());
			username = (String) sInput.readObject();
			server.display(username + " Connected.");
			sOutput.writeObject(new ServerClientChatMessage(sortedArray()));
		} catch (IOException e) {
			server.display("Exception creating new Input/output Streams: " + e);
			return;
		} catch (ClassNotFoundException e) {
		}
		date = new Date().toString() + "\n";
	}

	public String getUsername() {
		return username;
	}

	public long getId() {
		return id;
	}

	public boolean writeMsg(String msg) {
		if (!socket.isConnected()) {
			disconnect();
			return false;
		}
		try {
			sOutput.writeObject(new ServerClientChatMessage(msg,
					ServerClientChatMessage.MESSAGE));
		}
		// if an error occurs, do not abort just inform the user
		catch (IOException e) {
			server.display("Error sending message to " + username);
			server.display(e.toString());
		}
		return true;
	}

	public boolean writeMsg(ArrayList<ServerParameters> msg) {
		if (!socket.isConnected()) {
			disconnect();
			return false;
		}
		try {
			sOutput.writeObject(new ServerClientChatMessage(msg));
		}
		// if an error occurs, do not abort just inform the user
		catch (IOException e) {
			server.display("Error sending message to " + username);
			server.display(e.toString());
		}
		return true;
	}

	public void run() {
		boolean keepGoing = true;
		// broadcast(username + " new client connected");
		while (keepGoing) {
			// read a String (which is an object)
			try {
				cm = (ClientServerChatMessage) sInput.readObject();
			} catch (IOException e) {
				server.display(username + " Exception reading Streams: " + e);
				break;
			} catch (ClassNotFoundException e2) {
				break;
			}
			// the message part of the ChatMessage
			// String message = cm.getMessage();

			// Switch on the type of message receive
			switch (cm.getType()) {

			case ClientServerChatMessage.MESSAGE:
				server.setSSMsg(new ServerServerMessage(cm, username, server
						.getMyServerParameters()));
				// server.broadcast(username + ": " + message);
				break;
			case ClientServerChatMessage.LOGOUT:
				server.display(username + " disconnected with a LOGOUT message.");
				keepGoing = false;
				break;
			case ClientServerChatMessage.WHOISIN:
				writeMsg("List of the users connected at "
						+ server.getSimpleDateFormat().format(new Date()) + "\n");
				// scan al the users connected
				ArrayList<ClientThreadIF> clietThread = server.getClietThread();
				for (int i = 0; i < clietThread.size(); ++i) {
					ClientThreadIF ct = clietThread.get(i);
					writeMsg((i + 1) + ") " + ct.getUsername() + " since " + ct.getDate());
				}
				break;
			}
		}

		server.remove(id);
		disconnect();
	}

	// try to close everything
	public void disconnect() {
		try {
			if (sOutput != null)
				sOutput.close();
		} catch (Exception e) {
		}
		try {
			if (sInput != null)
				sInput.close();
		} catch (Exception e) {
		}
		;
		try {
			if (socket != null)
				socket.close();
		} catch (Exception e) {
		}
	}

	public String getDate() {
		return date;
	}

	public boolean allowCoonection() {
		int nc = server.getMyServerParameters().getNumberOfClients();
		for (ServerParameters servers : server.getMyServerList()) {
			if (nc > servers.getNumberOfClients()) {
				return false;
			}
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	public ArrayList<ServerParameters> sortedArray() {
		ArrayList<ServerParameters> tmpList = new ArrayList<ServerParameters>();
		for (ServerParameters serverParameters : server.getMyServerList()) {
			ServerParameters tmpParameters = new ServerParameters(
					serverParameters.getAddress(), serverParameters.getPort(),
					serverParameters.getNumberOfClients());
			tmpParameters.setRingNumber(serverParameters.getRingNumber());
			tmpList.add(tmpParameters);
		}

		Collections.sort(tmpList, new NumberOfclients());
		return tmpList;
	}

	@SuppressWarnings("rawtypes")
	class NumberOfclients implements Comparator {

		public int compare(Object server1, Object server2) {

			/*
			 * parameter are of type Object, so we have to downcast it to
			 * ServerParameters objects
			 */

			int nc1 = ((ServerParameters) server1).getNumberOfClients();
			int nc2 = ((ServerParameters) server2).getNumberOfClients();

			if (nc1 > nc2) {
				return 1;
			} else if (nc1 < nc2) {
				return -1;
			} else {
				return 0;
			}
		}

	}

}